package fromto

import (
	"errors"
	"io"
	"net"
	"time"
)

type Client struct {
	server         net.Conn
	pos            net.Conn
	timeout        int64
	dataToServer   []byte
	dataFromServer []byte
	addr           string
}

func New(address string, pos net.Conn) (*Client, error) {
	if address == "" || pos == nil {
		return nil, errors.New("invalid param for fromto.New")
	}

	cli := &Client{
		timeout: 50,
		addr:    address}
	cli.pos = pos

	timeout := time.Now().Add(time.Duration(int64(time.Second) * cli.timeout))
	cli.pos.SetDeadline(timeout)

	return cli, nil
}

func (cli *Client) Connect() error {
	var err error
	cli.server, err = net.Dial("tcp", cli.addr)
	if err != nil {
		return err
	}
	timeout := time.Now().Add(time.Duration(int64(time.Second) * cli.timeout))
	cli.server.SetDeadline(timeout)

	return nil
}

func (cli *Client) RecvFromPos() error {
	lenSection, err := readn(cli.pos, 2)
	if err != nil {
		return err
	}
	if len(lenSection) != 2 {
		remote := cli.pos.RemoteAddr().String()
		return errors.New("from[" + remote + "]  接收长度错误")
	}
	length := int(lenSection[0])*256 + int(lenSection[1])
	out, err := readn(cli.pos, length)
	if err != nil {
		return err
	}
	if len(out) != length {
		remote := cli.pos.RemoteAddr().String()
		return errors.New("from[" + remote + "]  接收数据错误")
	}

	cli.dataToServer = lenSection
	cli.dataToServer = append(cli.dataToServer, out...)
	return nil
}

func (cli *Client) SendToServer() error {
	return writen(cli.server, cli.dataToServer)
}

func (cli *Client) RecvFromServer() error {
	lenSection, err := readn(cli.server, 2)
	if err != nil {
		return err
	}
	if len(lenSection) != 2 {
		remote := cli.server.RemoteAddr().String()
		return errors.New("from[" + remote + "]  接收长度错误")
	}
	length := int(lenSection[0])*256 + int(lenSection[1])
	out, err := readn(cli.server, length)
	if err != nil {
		return err
	}
	if len(out) != length {
		remote := cli.server.RemoteAddr().String()
		return errors.New("from[" + remote + "]  接收数据错误")
	}

	cli.dataFromServer = lenSection
	cli.dataFromServer = append(cli.dataFromServer, out...)
	return nil
}

func (cli *Client) SendToPos() error {
	return writen(cli.pos, cli.dataFromServer)
}
func (cli *Client) GetData() ([]byte, []byte) {
	return cli.dataToServer, cli.dataFromServer
}

func (cli *Client) Close() {
	cli.server.Close()
}

func readn(conn net.Conn, max int) ([]byte, error) {
	out := make([]byte, 0, 2048)
	buf := make([]byte, max)

	left := max

	for left > 0 {
		n, err := conn.Read(buf)
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		out = append(out, buf[:n]...)
		left -= n
	}
	return out, nil
}

func writen(conn net.Conn, data []byte) error {
	left := len(data)
	max := len(data)

	for left > 0 {
		n, err := conn.Write(data[max-left:])
		if err != nil {
			return err
		}
		left -= n
	}
	return nil
}
